Skip to content

Conversation

FerroLx
Copy link

@FerroLx FerroLx commented Oct 2, 2025

Patches the input schema of MCP tools during conversion to LangChain tools. This change explicitly adds type: "string" to any schema property that defines an enum but lacks a type.

This resolves a google.api_core.exceptions.InvalidArgument error raised by the Google GenAI API, which requires enums to have a declared type.

Patches the input schema of MCP tools during conversion to LangChain tools. This change explicitly adds type: "string" to any schema property that defines an enum but lacks a type.

This resolves a google.api_core.exceptions.InvalidArgument error raised by the Google GenAI API, which requires enums to have a declared type.
msg = "Either a session or a connection config must be provided"
raise ValueError(msg)

input_schema = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FerroLx thanks for the PR: The place to fix this is likely langchain_core. Could you check if the issue exists in langchain_core / if you can reproduce it there?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you are correct!

the error is from the core.

it reproduces with this script

import os
from dotenv import load_dotenv
from langchain_core.tools import StructuredTool
from langchain_google_genai import ChatGoogleGenerativeAI

load_dotenv()

# This buggy schema simulates a tool definition from an external source
# where the 'type' for an enum is missing.
buggy_schema = {
    "title": "MyTool",
    "description": "A tool with a buggy enum schema.",
    "type": "object",
    "properties": {
        "param": {
            "description": "A parameter with an enum but no type.",
            "enum": ["a", "b"],
        }
    },
    "required": ["param"],
}


def my_tool_function(param: str):
    """A simple tool to demonstrate the issue."""
    return f"You selected {param}"


try:
    # We define the tool directly with the buggy dictionary schema
    tool = StructuredTool(
        name="MyTool",
        description="A tool with a buggy enum schema.",
        func=my_tool_function,
        args_schema=buggy_schema,
    )

    # Initialize the Vertex AI Chat Model
    # Make sure you are authenticated with Google Cloud CLI
    # and have set your project in the environment variables.
    # e.g., os.environ["GCLOUD_PROJECT"] = "your-gcloud-project"
    model = ChatGoogleGenerativeAI(model="gemini-2.5-flash")

    # Bind the tool to the model
    model_with_tools = model.bind_tools([tool])

    # This will raise an error when the tool schema is processed by the Google API
    print("Invoking model with tool...")
    model_with_tools.invoke("Select option a")

except Exception as e:
    print(f"Successfully reproduced the bug with langchain-google: {e}")

I've found the solution!

It's just do this!

def _convert_json_schema_to_openai_function(
    schema: dict,
    *,
    name: Optional[str] = None,
    description: Optional[str] = None,
    rm_titles: bool = True,
) -> FunctionDescription:
    """Converts a Pydantic model to a function description for the OpenAI API.

    Args:
        schema: The JSON schema to convert.
        name: The name of the function. If not provided, the title of the schema will be
            used.
        description: The description of the function. If not provided, the description
            of the schema will be used.
        rm_titles: Whether to remove titles from the schema. Defaults to True.

    Returns:
        The function description.
    """
    schema = dereference_refs(schema)
    if "definitions" in schema:  # pydantic 1
        schema.pop("definitions", None)
    if "$defs" in schema:  # pydantic 2
        schema.pop("$defs", None)

    # FIX: Ensure that any property with an 'enum' also has a 'type'
    if "properties" in schema:
        for prop in schema["properties"].values():
            if "enum" in prop and "type" not in prop:
                prop["type"] = "string"

    title = schema.pop("title", "")
    default_description = schema.pop("description", "")
    return {
        "name": name or title,
        "description": description or default_description,
        "parameters": _rm_titles(schema) if rm_titles else schema,
    }

Do you want me to do a pr for it or you'll do it?

@eyurtsev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants